package cn.jtfadmin.activiti.service.impl;

import cn.jtfadmin.activiti.domain.dto.FormDTO;
import cn.jtfadmin.activiti.service.ActFormService;
import cn.jtfadmin.base.lang.common.domain.dto.NameValueDTO;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.FormService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.TaskService;
import org.activiti.engine.form.*;
import org.activiti.engine.impl.ServiceImpl;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.form.DefaultTaskFormHandler;
import org.activiti.engine.impl.form.TaskFormHandler;
import org.activiti.engine.impl.interceptor.CommandExecutor;
import org.activiti.engine.impl.persistence.entity.DeploymentEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntityImpl;
import org.activiti.engine.impl.util.ProcessDefinitionUtil;
import org.activiti.engine.repository.ProcessDefinition;
import org.apache.commons.collections.map.HashedMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 表单服务实现类
 * @author jtf
 * @since 0.0.2
 */
@Service
public class ActFormServiceImpl implements ActFormService {

    @Autowired
    protected RepositoryService repositoryService;

    @Autowired
    protected ObjectMapper objectMapper;

    @Autowired
    private FormService formService;


    @Override
    public FormDTO getStartFormInfoByDeploymentId(String deploymentId){
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
        String processDefinitionId = processDefinition.getId();
        StartFormData startFormData = formService.getStartFormData(processDefinitionId);
        FormDTO dto = transferFormData(startFormData);
        if(StringUtils.hasText(dto.getFormKey()) && !repositoryService.getDeploymentResourceNames(deploymentId).contains(dto.getFormKey())){
            dto.setFormKey(null);
        }
        dto.setDeploymentId(deploymentId);
        return dto;
    }

    @Override
    public FormDTO getTaskFormInfoByTaskId(String taskId){
        TaskFormData taskFormData = formService.getTaskFormData(taskId);
        FormDTO dto = transferFormData(taskFormData);
        if(StringUtils.hasText(dto.getFormKey()) && !repositoryService.getDeploymentResourceNames(taskFormData.getDeploymentId()).contains(dto.getFormKey())){
            dto.setFormKey(null);
        }
        dto.setDeploymentId(taskFormData.getDeploymentId());
        dto.setTaskId(taskId);
        return dto;
    }

    @Override
    public FormDTO getTaskFormInfonByProcessDefinitionIdAndTaskDefinitionKey(String processDefinitionId, String taskDefinitionKey,
                                                                             String taskExecutionId) {
        if(formService instanceof ServiceImpl){
            ServiceImpl service = (ServiceImpl) formService;
            CommandExecutor commandExecutor = service.getCommandExecutor();
            TaskFormData taskForm = commandExecutor.execute(new GetTaskFormCmd(processDefinitionId, taskDefinitionKey, taskExecutionId));
            if(taskForm == null){
                return null;
            }
            FormDTO formDTO = transferFormData(taskForm);
            formDTO.setDeploymentId(taskForm.getDeploymentId());
            return formDTO;
        }


        return null;
    }

    @Override
    public void startForm(String deploymentId, String formData) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
        String processDefinitionId = processDefinition.getId();
        formService.submitStartFormData(processDefinitionId, formDataToMap(formData));
    }

    private Map<String, String> formDataToMap(String formData){
        if(StringUtils.hasText(formData)){
            JsonNode jsonNode = null;
            try {
                jsonNode = objectMapper.readTree(formData);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
            Map<String, String> properties = new HashedMap();
            Iterator<String> stringIterator = jsonNode.fieldNames();
            while (stringIterator.hasNext()){
                String next = stringIterator.next();
                JsonNode jsonNode1 = jsonNode.get(next);
                String s = jsonNode1.asText("");
                properties.put(next, s);
            }
            return properties;
        }
        return Collections.emptyMap();
    }

    @Override
    @Deprecated
    public void taskFormSubmit(String taskId, String formData) {
            formService.submitTaskFormData(taskId, formDataToMap(formData));
    }


    private FormDTO transferFormData(FormData formData){
        FormDTO dto = new FormDTO();
        dto.setFormKey(formData.getFormKey());
        List<FormProperty> formProperties = formData.getFormProperties();
        if(CollectionUtils.isEmpty(formProperties)){
            return dto;
        }
        List<FormDTO.FormPropertyDTO> list = formProperties.stream().map(t->{
            FormDTO.FormPropertyDTO d = new FormDTO.FormPropertyDTO();
            d.setId(t.getId());
            d.setName(t.getName());
            d.setValue(t.getValue());
            d.setReadable(t.isReadable());
            d.setRequired(t.isRequired());
            d.setWritable(t.isWritable());
            FormType type = t.getType();
            String typeName = type.getName();
            d.setType(typeName);
            if("date".equals(typeName)){
                Object datePattern = type.getInformation("datePattern");
                if(Objects.nonNull(datePattern))
                d.setDatePattern(datePattern.toString());
            }
            if("enum".equals(typeName)){
                Object values = type.getInformation("values");
                if(Objects.nonNull(values)){
                    Map<String,String> map = (Map<String, String>) values;

                    d.setEnmuDatas(map.entrySet().stream()
                    .map((entry)->new NameValueDTO(entry.getValue(),entry.getKey()))
                            .collect(Collectors.toList())
                    );
                }
            }

            return d;
        }).collect(Collectors.toList());
        dto.setFormProperties(list);
        return dto;

    }
}
